package com.pzh.assignment.controller;

import cn.hutool.core.lang.Assert;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.pzh.assignment.common.bean.Result;
import com.pzh.assignment.common.bean.param.TaskParam;
import com.pzh.assignment.common.bean.vo.TaskVO;
import com.pzh.assignment.common.bean.vo.TaskWorkloadVO;
import com.pzh.assignment.common.shiro.ShiroUtils;
import com.pzh.assignment.entity.Class;
import com.pzh.assignment.entity.Grade;
import com.pzh.assignment.entity.Groups;
import com.pzh.assignment.entity.Plan;
import com.pzh.assignment.entity.Program;
import com.pzh.assignment.entity.Role;
import com.pzh.assignment.entity.Task;
import com.pzh.assignment.entity.User;
import com.pzh.assignment.service.IClassService;
import com.pzh.assignment.service.IGradeService;
import com.pzh.assignment.service.IGroupsService;
import com.pzh.assignment.service.IPlanService;
import com.pzh.assignment.service.IProgramService;
import com.pzh.assignment.service.ITaskService;
import com.pzh.assignment.service.IUserService;
import org.apache.shiro.authz.annotation.Logical;
import org.apache.shiro.authz.annotation.RequiresRoles;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import java.text.DecimalFormat;
import java.util.HashMap;
import java.util.List;
import java.util.stream.Collectors;

/**
 * <p>
 *  前端控制器
 * </p>
 *
 * @author pengzhihao
 * @since 2022-04-16
 */
@RestController
@RequestMapping("/task")
public class TaskController {

    @Autowired
    ITaskService taskService;
    @Autowired
    IPlanService planService;
    @Autowired
    IProgramService programService;
    @Autowired
    IGradeService gradeService;
    @Autowired
    IClassService classService;
    @Autowired
    IGroupsService groupsService;
    @Autowired
    IUserService userService;

    @RequiresRoles(value = {"sys_admin", "admin"}, logical = Logical.OR)
    @PostMapping
    public Result save(@RequestBody TaskParam param) {
        Task task = Task.valueOf(param);
        task.setMerge(isMerge(param.getPlanId(), param.getActualNum()));
        taskService.save(task);
        updateRemainNum(param.getPlanId(), param.getActualNum());
        return Result.success(null);
    }


    @GetMapping("/page")
    public Result listTaskWorkloadVOByPage(@RequestParam(defaultValue = "1") Integer currentPage,
                                            @RequestParam(defaultValue = "5") Integer size) {
        Page<Task> page = new Page<>(currentPage, size);
        QueryWrapper<Task> taskQueryWrapper = initTaskWrapper();
        IPage pageData = taskService.page(page, taskQueryWrapper);
        List<TaskWorkloadVO> voList = ((IPage<Task>) pageData).getRecords().stream().map(TaskWorkloadVO::valueOf).collect(Collectors.toList());
        calculate(voList);
        pageData.setRecords(voList);
        return Result.success(pageData);
    }

    @GetMapping("/total")
    public Result totalTaskWorkloadVO() {
        QueryWrapper<Task> taskQueryWrapper = initTaskWrapper();
        List<Task> taskList = taskService.list(taskQueryWrapper);
        List<TaskWorkloadVO> voList = taskList.stream().map(TaskWorkloadVO::valueOf).collect(Collectors.toList());
        calculate(voList);
        return Result.success(voList);
    }

    @GetMapping
    public Result findTaskVOS(@RequestParam Integer teacherId) {
        List<TaskVO> voList = taskService.listTaskVO(teacherId);
        return Result.success(voList);
    }

    @RequiresRoles(value = {"sys_admin", "admin"}, logical = Logical.OR)
    @DeleteMapping
    public Result delete(@RequestParam Integer id) {
        Task task = taskService.getById(id);
        Plan plan = planService.getById(task.getPlanId());
        //更新教学计划中的剩余人数
        int oldNum = plan.getRemainNum();
        int newNum =oldNum + task.getActualNum();
        plan.setRemainNum(newNum);
        planService.updateById(plan);
        //删除当前教学任务
        taskService.removeById(id);
        //当教学计划中剩余人数从无变成有时需要将培养方案中的分配状态修改为未完成
        if (oldNum == 0 && newNum > 0) {
            Program program = new Program();
            program.setId(plan.getProgramId());
            program.setStatus(0);
            programService.updateById(program);
        }
        return Result.success(null);
    }


    /**
     * @description:  判断是否需要进行合班, 合班返回 1，不合班返回 0
     * @param: planId
     * @param: actualNum
     * @return: int
     * @author pengzhihao
     * @date: 2022/4/22
     */
    private int isMerge(Integer planId, Integer actualNum) {
        Plan plan = planService.getById(planId);
        Program program = programService.getById(plan.getProgramId());
        Grade grade = gradeService.getById(program.getGradeId());
        HashMap<String, Integer> eqMap = new HashMap<>();
        eqMap.put("major_id", program.getMajorId());
        eqMap.put("grade", grade.getGrade());
        List<Class> classList = classService.list(new QueryWrapper<Class>().allEq(eqMap));
        //找出对应年级专业中行政班级人数最大值
        Integer max = classList.stream().map(one -> one.getPeopleNum()).max(Integer::max).get();
        if (actualNum > max) {
            //教学班人数大于人数最多的行政班人数，需要合班
            return 1;
        } else {
            //不需要合班
            return 0;
        }
    }

    /**
     * @description:  更新教学计划表中剩余人数字段
     * @param: planId
     * @param: actualNum
     * @return: void
     * @author pengzhihao
     * @date: 2022/4/22
     */
    private void updateRemainNum(Integer planId, Integer actualNum) {
        Plan plan = planService.getById(planId);
        Assert.isTrue(plan.getRemainNum() >= actualNum, "剩余未安排学生人数不足");
        int newNum = plan.getRemainNum() - actualNum;
        plan.setRemainNum(newNum);
        planService.updateById(plan);
        //当教学计划中剩余未分配人数为0时将培养方案中的分配状态修改为已完成
        if (newNum == 0) {
            Program program = new Program();
            program.setId(plan.getProgramId());
            program.setStatus(1);
            programService.updateById(program);
        }
    }

    /**
     * @description: 计算教学任务工作量
     * @param: voList
     * @return: void
     * @author pengzhihao
     * @date: 2022/4/25
     */
    private void calculate(List<TaskWorkloadVO> voList) {
        DecimalFormat df = new DecimalFormat("#.##");
        for (TaskWorkloadVO taskWorkloadVO : voList) {
            List<TaskVO> taskVOS = taskService.listTaskVO(taskWorkloadVO.getTeacherId());
            float workload = 0;
            for (TaskVO taskVO : taskVOS) {
                Integer teacheNum = taskVO.getActualNum();//获取教学班实际人数
                Integer mostNum = taskVO.getMaxNum();//获取教学班上限人数
                Integer commonNum = taskVO.getCommonNum();//获取标准班人数
                float credit = taskVO.getPlan().getProgram().getCredit();//获取学分
                float workNumber = (float) Math.max(Math.min( teacheNum, mostNum), commonNum)/(float) commonNum * credit;
                workload += workNumber;
            }
            taskWorkloadVO.setWorkload(Float.parseFloat(df.format(workload)));
        }
    }

    private QueryWrapper<Task> initTaskWrapper(){
        Role role = ShiroUtils.getProfile().getRole();
        Integer groupId = ShiroUtils.getProfile().getGroupId();
        QueryWrapper<Task> wrapper = new QueryWrapper<>();
        switch (role.getName()) {
            case Role.SYSTEM_ADMIN:{
                wrapper.groupBy("teacher_id");
                break;
            }
            case Role.ORG_ADMIN:{
                Groups group = groupsService.getById(groupId);
                List<Groups> groupsList = groupsService.list(new QueryWrapper<Groups>().eq("college_id", group.getCollegeId()));
                List<Integer> groupIds = groupsList.stream().map(groups -> groups.getId()).collect(Collectors.toList());
                List<User> userList = userService.list(new QueryWrapper<User>().in("group_id", groupIds));
                List<Integer> teacherIds = userList.stream().map(user -> user.getId()).collect(Collectors.toList());
                wrapper.in("teacher_id", teacherIds).groupBy("teacher_id");
                break;
            }
            case Role.ADMIN:{
                List<User> userList = userService.list(new QueryWrapper<User>().eq("group_id", groupId));
                List<Integer> teacherIds = userList.stream().map(user -> user.getId()).collect(Collectors.toList());
                wrapper.in("teacher_id", teacherIds).groupBy("teacher_id");
                break;
            }
            case Role.TEACHER:{
                Integer teacherId = ShiroUtils.getProfile().getId();
                wrapper.eq("teacher_id", teacherId).groupBy("teacher_id");
            }
        }
        return wrapper;
    }

}
